<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>单例模式</title>
</head>

<p>单例模式的定义: 保证一个类仅有一个实例，并提供一个访问它的全局访问的</p>
<p>有一些对象 我们往往只需要一个， 比如线程池、全局缓存、浏览器中的window 对象等。</p>
<p>比如当我们点击一个按钮 会弹出一个弹窗 而这个弹窗是唯一的，无论点击多少次 这个浮窗都只会被创建一次， 那么这个浮窗就适合用单例模式来创建</p>
<button id="loginBtn">登陆</button>
<button id="loginBtn2">登陆2</button>
<button id="loginBtn3">登陆3</button>
<body>
    <script>
        // 要实现一个标准的单例模式并不复杂 无非就是用一个变量来标识当前是否已经为某个类创建过对象
        // 如果是 就在下次获取该类的实例时直接返回 之前创建的对象
        var Singleton = function(name) {
            this.name = name;
            this.instance = null
        }
        Singleton.prototype.getName = function() {
            console.log('获取name', this.name);
        }

        Singleton.getInstance = function(name) {
            if (!this.instance) {
                this.instance = new Singleton(name)
            }
            return this.instance
        }
        var a = Singleton.getInstance('ssss')
        var b = Singleton.getInstance('vvvvvv')
        console.log(b === a);
        // 虽然已经完成一个单例模式 但这段代码的意义并不大

        // 透明的单例模式
        // 我们现在的目标是实现一个透明的单例模式 用户从这个类中创建对象
        // 可以像使用其他任何普通类一样 我们将使用 CreateDiv 单例类 它的作用
        // 是负责在页面中创建唯一div节点 代码如下:
        
        var CreateDiv = (function() {
            var instance;
            
            var CreateDiv = function( html ) {
                if (instance) return instance;
                this.html = html;
                this.init();
                return instance = this
            }

            CreateDiv.prototype.init = function() {
                var div = document.createElement('div');
                div.innerHTML = this.html;
                document.body.appendChild( div )
            }
            return CreateDiv

        })();
        var aa = new CreateDiv('div1');
        var bb = new CreateDiv('div2')
        console.log('aa === bb', aa === bb);
        // 用代理实现单例模式
        // 将刚刚负责单例模式的代码移除
        let CreateDiv2 = function(html) {
            this.html = html;
            this.init()
        }
        CreateDiv2.prototype.init = function() {
            var div = document.createElement('div');
            div.innerHTML = this.html;
            document.body.appendChild(div)
        }

        // 代理类
        var ProxySingletonCreateDiv = (function() {
            var instance;
            return function (html) {
                if (!instance) {
                    instance = new CreateDiv( html )
                }
                return instance
            }
        })()

        var aaa = new ProxySingletonCreateDiv('aaa')
        var bbb = new ProxySingletonCreateDiv('bbb')
        console.log('aaa === bbb', aaa === bbb);
        // 通过代理模式实现了 单例模式  但是更多的是面向对象语言的做法
        // 4.4 JavaScript中的单例模式
        // JavaScript是一门无类语言 生搬单例模式的概念并没有什么意思
        // JavaScript中创建对象的方法十分简单  


        // 4.5 惰性单例

        
        let loginLayer = (function() {
            var div = document.createElement('div');
            div.innerHTML = '我是登陆浮窗';
            div.style.display = 'none';
            console.log('执行了 loginLayer =>  我并未点击就创建了');
            document.body.appendChild(div)
            return div
        })()

        document.getElementById('loginBtn').onclick = function() {
            loginLayer.style.display = 'block'
        }

        // 以上实现会有这个问题 比如我们就是进来看看 并没打算登陆 
        // 我们需要改写一下需要登陆的时候才创建弹窗

        let createLoginLayer = (function() {
            var div;
            return function() {
                if (!div) {
                    console.log('我被执行了 => div2');
                    div = document.createElement('div');
                    div.innerHTML = '我是惰性创建的单例登陆船 2 '
                    div.style.display = 'none';
                    document.body.appendChild(div)
                }
                return div
            }
        })()

        document.getElementById('loginBtn2').onclick = function() {
            let loginLayer = createLoginLayer()
            loginLayer.style.display = 'block'
        }

        // 4.6 通用的惰性单例
        // 我们可以发现上的代码虽然可以解决业务逻辑  但是明显违反了单一职责原则
        // 假设我们要创建 script呢？ 或者其他标签呢 那么明显刚刚那么做是不行的

        // 把不变的隔离

        let getSingle = function( fn ) {
            var result;
            return function() {
                // 如果有就直接返回  否则就执行回调
                return result || ( result = fn.apply(this, arguments ) )
            }
        }
        var cDiv = function() {
            var div = document.createElement('div');
            div.innerHTML = '我是登陆浮窗 3333';
            div.style.display = 'none';
            console.log('执行了 单一职责惰性div');
            document.body.appendChild(div)
            return div 
        }
        let div = getSingle(cDiv)

        document.getElementById('loginBtn3').onclick = function() {
            let loginLayer = div()
            loginLayer.style.display = 'block'
        }
    </script>
</body>
</html>